package structure;

import java.util.ArrayList;
import algorithm.*;
/*
 * Una grammatica è una 4-tupla <Vt, Vn, P, S >
 * Vt è l'insieme dei terminali
 * Vn èl'insieme dei non terminali
 * P è l'insieme delle produzioni
 * S è l'assioma
 */

public class grammar {
	
	private 
		ArrayList<String> VT;
		ArrayList<String> VN;
		String S;
		ArrayList<production> P;
		
	//costruttori
	public	grammar(){
		
		this.VT= new ArrayList<String>();
		this.VN= new ArrayList<String>();
		this.S= "";
		this.P= new ArrayList<production>();
	}
	
	public	grammar(ArrayList<String> paramVT, ArrayList<String> paramVN, String paramS, ArrayList<production> paramP){
		
		this.setVT(paramVT);
		this.setVN(paramVN);
		this.setS(paramS);
		this.setP(paramP);
	}
		
	public	grammar(String paramG){

		//come da traccia prendo il primo simbolo come se fosse l'assioma
		this.S= paramG.substring(0, 1);
		this.VT= new ArrayList<String>();
		this.VN= new ArrayList<String>();
		this.P= new ArrayList<production>();
		
		this.createP(paramG);
		
		this.createVN(this.P);
		
		this.createVT(this.P);
	}
	
	public grammar(grammar g){
		
		this.P= g.getAllProduction();
		this.VT= g.getVT();
		this.VN= g.getVN();
		this.S= g.getS();
	}
	//
		
	//crea l'insieme delle produzioni
	public	void createP(String paramG){
		
		this.P.clear();
		int lastIndex= 0;
		String sub= "";
		
		for(int i=0; i<paramG.length();i++){
			
			if(paramG.substring(i, i+1).equals(";") || paramG.substring(i, i+1).equals(".")){
				
				sub= paramG.substring(lastIndex, i);
				lastIndex= i+1;
				this.addProduction(sub);
			}
		}
	}
	
	public void createP(ArrayList<production> productions){

		//System.out.println("Inizio create P");
		this.P.clear();
		for(production p : productions){
			
			this.P.add(p);
		}
		//System.out.println("Fine create P");
	}
	
	//crea l'insieme dei terminali
	public void createVT(ArrayList<production> productions){
		
		this.VT.clear();
		for (production p : productions){
			
			//popolo l'array che rappresenta i terminali
			ArrayList<String> tmpVT= p.getLower();
			for(String vt : tmpVT){
				
				if(!this.VT.contains(vt)) this.VT.add(vt);
			}
		}
	}
	
	//crea l'insieme dei non terminali
	public void createVN(ArrayList<production> productions){
		
		this.VN.clear();
		for (production p : productions){
			
			//popolo l'array che rappresenta l'insieme dei non terminali
			ArrayList<String> tmpVN= p.getUpper();
			for(String vn : tmpVN){
				
				if(!this.VN.contains(vn)) this.VN.add(vn);
			}
		}
			
	}
	
	//crea l'assioma
	public void createS(ArrayList<production> productions){
		
		this.S= productions.get(0).getLeftSide();
	}
	
	//crea l'ntera grammatica partendo dall'array delle produzioni
	public void createItAll(ArrayList<production> productions){
		
		this.createP(productions);
		this.createVN(productions);
		this.createVT(productions);
		this.createS(productions);
	}
		
	//aggiungo le produzioni alla creazione della struttura
	public	void addProduction(String paramP){
			
		if(paramP == null) System.out.println("sottostringa in addProduction è nulla");
		else{
			
			paramP+= "|";
			production tmpP= new production(paramP);
			this.P.add(tmpP);
		}
	}
	
	//aggiungo le produzioni anche dopo la creazione della struttura
	public void addProductionAfter(String leftside, String singleProduction){

		ArrayList<production> productions= this.getAllProduction();
		ArrayList<String> allLeftside= utility.getAllLeftSide(productions);
		if(allLeftside.contains(leftside)){
			
			production p= utility.getProductionByLeftSide(leftside, productions);
			/*ArrayList<String> rightside= p.getRightSide();
			rightside.add(singleProduction);*/
			p.addProduction(singleProduction);
		}
	}
	
	//aggiungo un insieme di produzioni alla grammatica
	public void addAllProduction(ArrayList<production> productions){
		
		for(production p : productions){
			
			this.P.add(p);
		}
	}
	//
		
		//set
	public	void setVN(ArrayList<String> paramVN){
			
			this.VN= paramVN;
		}
		
	public	void setVT(ArrayList<String> paramVT){
			
			this.VT= paramVT;
		}
		
	public	void setS(String paramS){
			
			this.S= paramS;
		}
		
	public	void setP(ArrayList<production> paramP){
			
			this.P= paramP;
		}
		//
	
	//get
	public ArrayList<production> getAllProduction(){
		
		return this.P;
	}
	
	public ArrayList<String> getVT(){
		
		return this.VT;
	}
	
	public ArrayList<String> getVN(){
		
		return this.VN;
	}
	
	public String getS(){
		
		return this.S;
	}
	
	//restituisce l'ordine di una grammatica
	public int getGrammarOrder(grammar g){
		/*
		 * l'ordine di una grammatica è il massimo ordine di una produzione
		 */
		
		return 0;
	}
	
	//crea la grammatica aumentata
	public void augmentGrammar(){
		
		String[] aplphabet= {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 
									 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};

		String tmpS= "";
		//prendo un simbolo non terminale ancora non utilizzato
		for(String s : aplphabet){
			
			if(!this.VN.contains(s)) {
				tmpS= s;
				break;
			}
		}
		//mi salvo e rimuovo tutti gli elementi dall'array delle produzioni
		ArrayList<production> tmpProd= miscellaneous.cloneArray(this.P);
		this.P.clear();
		
		//aggiungo la produzione S'->S
		String tmpP= tmpS + "->" + this.S;
		this.addProduction(tmpP);
		
		//aggiungo tutte le produzioni di prima
		this.addAllProduction(tmpProd);
		
		//sostituisco il vecchio assioma con il nuovo assioma
		this.setS(tmpS);
	}
	//
	
	//DEBUG: stampo tutto
	public	void printItAll(){
	
		System.out.println("START GRAMMAR");
		System.out.println("axiom: "+ this.S);
		System.out.print("VT= ");
		for(String s : this.VT){
			
			System.out.print(s + ", ");
		}
		System.out.println("");

		System.out.print("VN= ");
		for(String s : this.VN){

			System.out.print(s + ", ");
		}
		System.out.println("");
	
		System.out.println("Production:");
		for(production s : this.P){
			
			s.printIt();
		}
		System.out.println("END GRAMMAR");
	}
	
	public void printProductionOnly(){
		
		System.out.println("Produzioni: ");
		for(production p : this.P){
			
			p.printRightside();
		}
	}

}
